﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/*  
 * Sample program for test updates fleets function.
 * Test cases are described in separate file
 */
namespace TestFleets
{
    public struct Fleet
    {
        public TimeSpan Span;
        public int Id;
        public int Owner;
        public int SId;
        public int PId;
        public int CS;
        public int TR;
        public int DS;
        public int CR;
        public int BS;
    }

    class Equations
    {
        public static int SbAV(int l)
        {
            return (int)Math.Ceiling(Math.Pow(l, 1.5) * (l * 1.0));
        }
        public static int SbDV(int l)
        {
            return (int)Math.Ceiling((Math.Pow(l, 2) + l * 7.0) * (l * 1.0) / 2.0);
        }
        private const int DsAV = 2, DsDV = 1;
        private const int CrAV = 8, CrDV = 16;
        private const int BsAV = 34, BsDV = 24;
        internal static bool AttackProcedure(double A1, double D1, int Phy1, int Math1, ref  int Ds1, ref int Cr1, ref int Bs1, double A2, double D2, int Phy2, int Math2, ref int Ds2, ref int Cr2, ref int Bs2, ref double SB, out int attCV, out int defCV, out double AttackerChance, out double WinningLosses)
        {
            if (Phy1 >= Phy2 + 6) A1 += 0.25;
            if (Math1 >= Math2 + 6) D1 += 0.25;
            if (Phy2 >= Phy1 + 6) A2 += 0.25;
            if (Math2 >= Math1 + 6) D2 += 0.25;
            bool winner = false;
            attCV = Ds1 * (DsAV + DsDV) + Cr1 * (CrAV + CrDV) + Bs1 * (BsAV + BsDV);
            defCV = Ds2 * (DsAV + DsDV) + Cr2 * (CrAV + CrDV) + Bs2 * (BsAV + BsDV) + SbAV((int)SB) + SbDV((int)SB);
            double AV1 = (Ds1 * DsAV + Cr1 * CrAV + Bs1 * BsAV) * A1 * (1 + Phy1 / 100.0);
            double AV2 = (Ds2 * DsAV + Cr2 * CrAV + Bs2 * BsAV + SbAV((int)SB)) * A2 * (1 + Phy2 / 100.0);
            double DV1 = (Ds1 * DsDV + Cr1 * CrDV + Bs1 * BsDV) * D1 * (1 + Math1 / 100.0);
            double DV2 = (Ds2 * DsDV + Cr2 * CrDV + Bs2 * BsDV + SbDV((int)SB)) * D2 * (1 + Math2 / 100.0);

            double CV1 = AV1 + DV1;
            double CV2 = AV2 + DV2;
            double chance = CV1 / (CV1 + CV2) + 0.01 * (Phy1 - Phy2);
            if (chance < 0.01) chance = 0;
            if (chance > 1) chance = 1;
            AttackerChance = chance;
            Random r = new Random();
            int l = r.Next(100);
            double loses;
            if (l < (int)(chance * 100))
            {
                winner = true;
                loses = (1 - AV2 / CV1) + 0.01 * (Math1 - Math2);
                if (loses < 0) loses = 0; //this may happen when player has slim chance to win
                if (loses > 1) loses = 1;
                Ds1 = (int)(loses * Ds1);
                Cr1 = (int)(loses * Cr1);
                Bs1 = (int)(loses * Bs1);
                Ds2 = 0; Cr2 = 0; Bs2 = 0; SB = 0;
            }
            else
            {
                winner = false;
                loses = (1 - AV1 / CV2) + 0.01 * (Math2 - Math1);
                if (loses < 0) loses = 0;
                if (loses > 1) loses = 1;
                Ds2 = (int)(loses * Ds2);
                Cr2 = (int)(loses * Cr2);
                Bs2 = (int)(loses * Bs2);
                SB = loses * SB;
                Ds1 = 0; Cr1 = 0; Bs1 = 0;
            }
            WinningLosses = 1 - loses;
            return winner;
        }
    }
    class UserDefinedFunctions
    {

        internal static int FleetOwner(int sId, int pId)
        {
            if (pId == 4) return 0;
            else if (pId == 5) return 3;
            else if (pId == 6) return 4;
            else if (sId == 3) return 100;
            else if (sId == 4) return 3;
            else if (pId == 2 && sId == 1) return 100;
            else return sId - 1;
        }

        internal static int GetPlayerInt(int owner, string p)
        {
            return 0;
        }

        internal static string UsedArtifact(int owner)
        {
            return null;
        }

        internal static float GetPlayerFloat(int owner, string p)
        {
            return 0;
        }

        internal static Dictionary<string, int> FleetOnPlanet(int sId, int pId)
        {
            Dictionary<string, int> tmp = new Dictionary<string, int>();
            if (pId == 2)
            {
                tmp.Add("ColonyShips", 0);
                tmp.Add("Transports", 0);
                tmp.Add("Destroyers", 1);
                tmp.Add("Crusiers", 0);
                tmp.Add("Battleships", 0);
                tmp.Add("Starbase", 0);
            }
            else if (pId >= 3)
            {
                tmp.Add("ColonyShips", 0);
                tmp.Add("Transports", 0);
                tmp.Add("Destroyers", 1000);
                tmp.Add("Crusiers", 0);
                tmp.Add("Battleships", 0);
                tmp.Add("Starbase", 0);
            }
            else
            {
                tmp.Add("ColonyShips", 0);
                tmp.Add("Transports", 0);
                tmp.Add("Destroyers", 0);
                tmp.Add("Crusiers", 0);
                tmp.Add("Battleships", 0);
                tmp.Add("Starbase", 0);
            }
            return tmp;
        }

        internal static int PlanetOwner(int sId, int pId)
        {
            if (sId == 1) return 0;
            else if (sId == 2) return 1;
            else if (sId == 3) return 100;
            else if (sId == 4) return 3;
            else return 0;
        }

        internal static bool IsEmptyPlanet(int sId, int pId)
        {
            if (sId == 1) return true;
            else return false;
        }

        internal static bool FleetExists(int sId, int pId)
        {
            if (pId == 1) return false;
            else return true;
        }

        internal static int PlayerPlanets(int owner)
        {
            if (owner == 3) return 5;
            else if (owner == 4) return 1;
            else return 5;
        }

        internal static string SystemName(int sId)
        {
            return "sID: " + sId.ToString();
        }

        internal static string PlayerName(int DefID)
        {
            return "pID:" + DefID.ToString();
        }

        internal static int PlayerCulture(int owner)
        {
            return 5;
        }

        internal static int PopulationOnPlanet(int sId, int pId)
        {
            return 5;
        }
    }
    class Program
    {
        #region Create
        private static Fleet CreateFleet(int owner, int sID, int pId)
        {
            Fleet f = new Fleet() { DS = 100, PId = pId, SId = sID, Owner = owner };
            return f;
        }
        private static Fleet CreateFleet(int owner, int sID, int pId, int tr)
        {
            Fleet f = new Fleet() { DS = 100, PId = pId, SId = sID, Owner = owner, TR = tr };
            return f;
        }
        private static Fleet CreateFleet(int owner, int sID, int pId, int tr, int cs)
        {
            Fleet f = new Fleet() { DS = 100, PId = pId, SId = sID, Owner = owner, TR = tr, CS = cs };
            return f;
        }
        private static Fleet FleetForTestCase(int no)
        {
            switch (no)
            {
                case 1: return CreateFleet(3, 1, 1);
                case 2: return CreateFleet(3, 1, 1, 1);
                case 3: return CreateFleet(3, 1, 1, 0, 5);
                case 4: return CreateFleet(4, 1, 1, 0, 5);
                case 5: return CreateFleet(3, 2, 1);
                case 6: return CreateFleet(3, 2, 1, 10);
                case 7: return CreateFleet(4, 2, 1, 10);
                case 8: return CreateFleet(3, 2, 1, 0, 10);
                case 9: return CreateFleet(3, 2, 1, 1, 1);
                case 10: return CreateFleet(4, 2, 1, 1, 1);
                case 11: return CreateFleet(3, 3, 1);
                case 12: return CreateFleet(3, 3, 2);
                case 13: return CreateFleet(3, 3, 1, 10);
                case 14: return CreateFleet(3, 3, 2, 10);
                case 15: return CreateFleet(4, 3, 1, 10);
                case 16: return CreateFleet(4, 3, 2, 10);
                case 17: return CreateFleet(4, 3, 1, 0, 10);
                case 18: return CreateFleet(3, 3, 1, 10, 10);
                case 19: return CreateFleet(3, 3, 2, 10, 10);
                case 20: return CreateFleet(4, 3, 1, 10, 10);
                case 21: return CreateFleet(4, 3, 2, 10, 10);
                case 22: return CreateFleet(3, 4, 1, 10);
                case 23: return CreateFleet(3, 4, 1, 10, 10);
                case 24: return CreateFleet(3, 1, 2, 10);
                case 25: return CreateFleet(3, 1, 2, 10, 10);
                case 26: return CreateFleet(3, 3, 4, 10);
                case 27: return CreateFleet(4, 3, 4, 10);
                case 28: return CreateFleet(3, 3, 5);
                case 29: return CreateFleet(3, 3, 5, 10);
                case 30: return CreateFleet(4, 3, 6, 10);
                case 31: return CreateFleet(3, 2, 5);
                case 32: return CreateFleet(3, 2, 5, 10);
                case 33: return CreateFleet(4, 2, 6, 10);
                default: return CreateFleet(3, 1, 1);

            }
        }
        #endregion
        static void Main(string[] args)
        {
            ConsoleColor f = Console.ForegroundColor;
            for (int i = 28; i < 34; i++)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Test case:" + i.ToString());
                Console.ForegroundColor = f;
                ProcessFleet(FleetForTestCase(i), 0, 0, 0, 15);
                Console.ReadLine();
            }
        }

        private static void ProcessFleet(Fleet a, int intervalTime, double AttackBonus, double DefBonus, int pp)
        {
            TimeSpan s = a.Span;
            int fid = a.Id;
            int owner = a.Owner;
            int sId = a.SId;
            int pId = a.PId;
            int cs = a.CS;
            int tr = a.TR;
            int ds = a.DS;
            int cr = a.CR;
            int bs = a.BS;
            // InsertNews(8, DateTime.Now, "Ts: " + sId.ToString() + " PS:" + pId.ToString() + " " + s.TotalSeconds.ToString());
            int FleetOwner = UserDefinedFunctions.FleetOwner(sId, pId);

            if (FleetOwner != owner && FleetOwner >= 1)//battle always come first
            {
                double AttackerAt, DefenderAt, AttackerDef, DefenderDef;
                int DefID = UserDefinedFunctions.FleetOwner(sId, pId);
                AttackerAt = 1 + AttackBonus * UserDefinedFunctions.GetPlayerInt(owner, "Attack");
                DefenderAt = 1 + AttackBonus * UserDefinedFunctions.GetPlayerInt(DefID, "Attack");
                AttackerDef = 1 + AttackBonus * UserDefinedFunctions.GetPlayerInt(owner, "Defense");
                DefenderDef = 1 + AttackBonus * UserDefinedFunctions.GetPlayerInt(DefID, "Defense");
                //get bonuses from artifacts
                if (UserDefinedFunctions.UsedArtifact(owner) != null)
                {
                    switch (UserDefinedFunctions.UsedArtifact(owner))
                    {
                        case "PG": AttackerAt += 0.07; break;
                        case "GG": AttackerAt += 0.14; break;
                        case "ES": AttackerDef += 0.08; break;
                        case "PS": AttackerDef += 0.16; break;
                        case "AF": AttackerAt += 0.15; AttackerDef += 0.15; break;
                    }
                }
                if (UserDefinedFunctions.UsedArtifact(DefID) != null)
                {
                    switch (UserDefinedFunctions.UsedArtifact(DefID))
                    {
                        case "PG": DefenderAt += 0.07; break;
                        case "GG": DefenderAt += 0.14; break;
                        case "ES": DefenderDef += 0.08; break;
                        case "PS": DefenderDef += 0.16; break;
                        case "AF": DefenderAt += 0.15; DefenderDef += 0.15; break;
                    }
                }
                int AttackerPhy = (int)UserDefinedFunctions.GetPlayerFloat(owner, "Physics");
                int AttackerMath = (int)UserDefinedFunctions.GetPlayerFloat(owner, "Math");
                int DefenderPhy = (int)UserDefinedFunctions.GetPlayerFloat(DefID, "Physics");
                int DefenderMath = (int)UserDefinedFunctions.GetPlayerFloat(DefID, "Math");
                Dictionary<string, int> fleetOnPlanet = UserDefinedFunctions.FleetOnPlanet(sId, pId);
                int defDs = fleetOnPlanet["Destroyers"];
                int defCr = fleetOnPlanet["Crusiers"];
                int defBs = fleetOnPlanet["Battleships"];
                double defSb = fleetOnPlanet["Starbase"];
                if (defDs + defCr + defBs + defSb > 0)//in case there is no fleet on planet do not start fight
                {
                    //and if win attack transports
                    int attCv, defCV; double attchance, winninglosses;
                    bool win = Equations.AttackProcedure(AttackerAt, AttackerDef, AttackerPhy, AttackerMath, ref ds, ref cr, ref bs,
                        DefenderAt, DefenderDef, DefenderPhy, DefenderMath, ref defDs, ref defCr, ref defBs, ref defSb, out attCv, out defCV, out attchance, out winninglosses);
                    InsertNews(8, DateTime.Now, "s:" + sId.ToString() + " p:" + pId.ToString() + " aCV:" + attCv.ToString() + " dCV:" + defCV.ToString() + " winner:" + win.ToString() + " loses:" + winninglosses.ToString());
                    if (win == true)
                    {

                        InsertNews(owner, DateTime.Now, "Congratulations! You have won battle on the orbit of: " + UserDefinedFunctions.SystemName(sId) + " #" + pId.ToString() + " against " + UserDefinedFunctions.PlayerName(DefID).ToString() + ".<br />\n" + "You gained " + defCV.ToString() + " experience.<br/>\n" +
                            "You lost about " + ((int)(winninglosses * 100)).ToString() + "% of your fleet.");
                        InsertNews(DefID, DateTime.Now, "Your fleet was defeated on the orbit of: " + UserDefinedFunctions.SystemName(sId) + " #" + pId.ToString() + " against " + UserDefinedFunctions.PlayerName(owner).ToString() + ".<br/>" +
                            "You killed about " + ((int)(winninglosses * 100)).ToString() + "% of enemy fleet.");
                        InsertBattle(owner, attCv, DefID, defCV, DateTime.Now, sId, pId, owner);
                        //Add fleet of attacker
                        SetExperience(owner, defCV);
                        FleetOwner = 0;
                    }
                    else
                    {
                        InsertNews(DefID, DateTime.Now, "Congratulations! Your defending forces have won battle on the orbit of: " + UserDefinedFunctions.SystemName(sId) + " #" + pId.ToString() + " against " + UserDefinedFunctions.PlayerName(owner).ToString() + ".<br />\n" + "You gained " + attCv.ToString() + " experience.<br/>" +
                            "You lost about " + ((int)(winninglosses * 100)).ToString() + "% of your fleet.");
                        InsertNews(owner, DateTime.Now, "Your fleet was defeated on the orbit of: " + UserDefinedFunctions.SystemName(sId) + " #" + pId.ToString() + " against " + UserDefinedFunctions.PlayerName(DefID).ToString() + ".<br/>" +
                            "You killed about " + ((int)(winninglosses * 100)).ToString() + "% of enemy fleet.");
                        InsertBattle(owner, attCv, DefID, defCV, DateTime.Now, sId, pId, DefID);
                        SetFleetWithCS(sId, pId, DefID, fleetOnPlanet["ColonyShips"], fleetOnPlanet["Transports"], defDs, defCr, defBs);
                        SetSbOnPlanet(sId, pId, defSb);
                        SetExperience(DefID, attCv);
                        RemoveFleet(fid);
                        return;
                    }
                }
            }

            bool CS = (cs != 0 ? true : false);
            bool TR = (tr != 0 ? true : false);
            bool UnknownPlanet = (UserDefinedFunctions.PlanetOwner(sId, pId) == 1 ? true : false);
            bool EnemyPlanet = (UserDefinedFunctions.PlanetOwner(sId, pId) != owner ? true : false);
            bool EmptyPlanet = UserDefinedFunctions.IsEmptyPlanet(sId, pId);
            bool FleetExists = UserDefinedFunctions.FleetExists(sId, pId);
            bool CanColonize = (UserDefinedFunctions.PlayerPlanets(owner) < UserDefinedFunctions.PlayerCulture(owner));
            bool SamePlayer = (UserDefinedFunctions.PlanetOwner(sId, pId) == owner);
            if (EmptyPlanet == true)
            {
                if (CanColonize == true && CS == true)
                {
                    ColonizePlanet(sId, pId, owner);
                    AddFleet(sId, pId, owner, tr, ds, cr, bs);
                    AddProductionPoints(sId, pId, (cs - 1) * pp);
                }
                else
                {
                    //if there is some fleet we should add it not delete
                    AddFleetWithCS(sId, pId, owner, cs, tr, ds, cr, bs);
                }
            }
            else if (SamePlayer == true)
            {
                AddFleet(sId, pId, owner, tr, ds, cr, bs);
                AddProductionPoints(sId, pId, cs * pp);
            }
            else if (UnknownPlanet == true || EnemyPlanet == true)
            {
                UseTransports(sId, pId, owner, cs, tr, ds, cr, bs);
                //if (CanColonize == true && TR == true)
                //{
                //    UseTransports
                //}
                //else
                //    SetFleetWithCS(sId, pId, owner, cs, tr, ds, cr, bs);
            }


            //remove fleet
            RemoveFleet(fid);
        }

        private static void RemoveFleet(int fid)
        {
            Console.WriteLine("Remove fleet");
            //throw new NotImplementedException();
        }

        private static void SetSbOnPlanet(int sId, int pId, double defSb)
        {
            Console.WriteLine("Set sb: " + sId.ToString() + " #" + pId.ToString());
        }

        private static void SetFleetWithCS(int sId, int pId, int DefID, int p, int p_5, int defDs, int defCr, int defBs)
        {
            Console.WriteLine("Set fleet with cs: " + sId.ToString() + " #" + pId.ToString());
        }

        private static void InsertBattle(int owner, int attCv, int DefID, int defCV, DateTime dateTime, int sId, int pId, int owner_8)
        {
            Console.WriteLine("Insert battle between: " + owner.ToString() + " vs. " + DefID.ToString());
        }

        private static void UseTransports(int sId, int pId, int owner, int cs, int tr, int ds, int cr, int bs)
        {
            //int pp = int.Parse(GetConfigurationForOption("PPpointsFromCS"));
            if (UserDefinedFunctions.IsEmptyPlanet(sId, pId) == false)
            {
                if (UserDefinedFunctions.PlayerPlanets(owner) < UserDefinedFunctions.PlayerCulture(owner))
                {
                    //conquer the planet
                    int pop = UserDefinedFunctions.PopulationOnPlanet(sId, pId);
                    if (pop <= tr)
                    {
                        ConquerPlanet(sId, pId, owner);
                        AddProductionPoints(sId, pId, cs * 15);
                        AddFleetWithCS(sId, pId, owner, 0, tr - pop, ds, cr, bs);
                    }
                }
                else
                {
                    //destroy pop
                    KillPopOnPlanet(sId, pId, owner, ref tr);
                    AddFleetWithCS(sId, pId, owner, cs, tr, ds, cr, bs);
                }
            }
        }

        private static void KillPopOnPlanet(int sId, int pId, int owner, ref int tr)
        {
            Console.WriteLine("Kill pop: " + sId.ToString() + " #" + pId.ToString());
        }

        private static void ConquerPlanet(int sId, int pId, int owner)
        {
            Console.WriteLine("Conquer planet: " + sId.ToString() + " #" + pId.ToString());
        }

        private static void AddFleetWithCS(int sId, int pId, int owner, int cs, int tr, int ds, int cr, int bs)
        {
            Console.WriteLine("Add fleet with cs: " + sId.ToString() + " #" + pId.ToString());
        }

        private static void AddProductionPoints(int sId, int pId, int p)
        {
            Console.WriteLine("Add pp: " + sId.ToString() + " #" + pId.ToString());
        }

        private static void AddFleet(int sId, int pId, int owner, int tr, int ds, int cr, int bs)
        {
            Console.WriteLine("AddFleet: " + sId.ToString() + " " + pId.ToString());
        }

        private static void ColonizePlanet(int sId, int pId, int owner)
        {
            Console.WriteLine("Colonize: " + sId.ToString() + " #" + pId.ToString());
        }

        private static void SetExperience(int owner, int defCV)
        {
            Console.WriteLine("SetExp: " + owner.ToString() + " exp: " + defCV);
        }

        private static void InsertNews(int p, DateTime dateTime, string p_3)
        {
            string output = "pId: " + p.ToString() + "\t" + dateTime.ToString() + "\t" + p_3;
            Console.WriteLine(output);
        }
    }
}